#include <dde_kit/timer.h>
#include "conf.h"

#include <eku/timer.h>
#include <eku/sync.h>
#include <eku/proc.h>
#include <eku/symbol.h>
#include <eku/system.h>

#include <inc/x86_lock.h>

spin_lock_t dk_timer_lock;
struct dde_kit_timer dk_timers[DK_TIMER_COUNT];
static int dkt_proc;

struct dde_kit_timer *
dde_kit_timer_add(void (*fn)(void *), void *priv,
				   unsigned long timeout)
{
	 struct dde_kit_timer *result = NULL;

	 spl_acquire(&dk_timer_lock);
		 
	 int i;
	 for (i = 0; i != DK_TIMER_COUNT; ++ i)
	 {
		  if (dk_timers[i].free)
		  {
			   result = dk_timers + i;
			   break;
		  }
	 }
	 if (result != NULL) result->free = 0;

	 spl_release(&dk_timer_lock);
	 
	 if (result == NULL) return NULL;

	 result->fn = fn;
	 result->priv = priv;

	 /* dirty hack */
	 unsigned long __timeout = (timeout - sysconf.jiffies_0) * 10;
	 result->timer = timer_open_ms(dkt_proc, __timeout);

	 return result;
}

/**
 * Delete timer
 *
 * \param timer  timer reference
 */
void
dde_kit_timer_del(struct dde_kit_timer *timer)
{
	 timer_close(timer->timer);
	 timer_free(timer->timer);
	 timer->free = 1;
}

/**
 * Check whether a timer is pending
 *
 * \param timer  timer reference
 */
int
dde_kit_timer_pending(struct dde_kit_timer *timer)
{
	 return timer_test(timer->timer);
}

struct dk_timer_priv_t
{
	 void (*init)(void *);
	 void  *init_data;
} dkt_priv;

static void
dk_timer_thread(void *priv)
{
	 printf("dk timer pid %d\n", cur_pid());
	 
	 dde_kit_thread_adopt_myself("DKTIMER");
	 while (1)
	 {
		  int i;
		  for (i = 0; i != DK_TIMER_COUNT; ++ i)
		  {
			   if (!dk_timers[i].free)
			   {
					// printf("Scan %d %d %d\n", i, dk_timers[i].timer, timer_test(dk_timers[i].timer));
					if (timer_test(dk_timers[i].timer) == 0)
					{
						 // printf("Calling timer %d, %08x\n", i, dk_timers[i].fn);
						 dk_timers[i].fn(dk_timers[i].priv);
						 // printf("Return\n");
					}
			   }
		  }

		  wait_try();
	 }
}

/** Init timers and ticks */
void
dde_kit_timer_init(void(*thread_init)(void *), void *priv)
{
	 dkt_priv.init = thread_init;
	 dkt_priv.init_data = priv;
	 dkt_proc = process_create_wait(&dk_timer_thread, 0, 8192);
	 notify_ex(dkt_proc, WLEVEL_START);
}

void
dde_kit_timer_symbol(void)
{
	 export_symbol("dde_kit_timer_ticks",   (void *)&timer_counter, 1);
	 export_symbol("jiffies",               (void *)&sysconf.jiffies_0, 1);
	 export_symbol("DDE_KIT_HZ",            (void *)&timer_freq, 1);
	 export_symbol("dde_kit_timer_add",     &dde_kit_timer_add, 1);
	 export_symbol("dde_kit_timer_del",     &dde_kit_timer_del, 1);
	 export_symbol("dde_kit_timer_pending", &dde_kit_timer_pending, 1);
	 export_symbol("dde_kit_timer_init",    &dde_kit_timer_init, 1);
}
